﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Security.Cryptography;

namespace Benchmark.Db.Base.DataCreation
{
    public class RandomHelper
    {
        private static string CHARS = "0123456789QWERTYUIOPASDFGHJKLZXCVBNM";
        private static string DIGITS = "0123456789";
        private static RNGCryptoServiceProvider seedProvider = new RNGCryptoServiceProvider();
        private static Random randomizer = new Random(DateTime.Now.Millisecond);

        public static string CreateRandomPassword()
        {
            Random random = new Random(DateTime.Now.Millisecond);
            return random.Next(100000, 900000).ToString();
        }

        public static string CreateValidationCode()
        {
            Random random = new Random(DateTime.Now.Millisecond);
            return random.Next(1000, 9000).ToString();
        }

        //TODO: parola sadece sayılardan oluşur ise mobil cihazlardan giriş yapmak kolaylaşır.
        /// <summary>
        /// Generates a random password
        /// </summary>
        /// <param name="length">length of the password</param>
        /// <returns></returns>
        public static string RandomText(int length)
        {
            string password_space = CHARS;
            StringBuilder sb = new StringBuilder(length);
            byte[] randomBytes = new byte[4];
            seedProvider.GetBytes(randomBytes);

            int seed = (randomBytes[0] & 0x7f) << 24 |
                        randomBytes[1] << 16 |
                        randomBytes[2] << 8 |
                        randomBytes[3];

            Random random = new Random(seed);
            for (int i = 0; i < length; i++)
            {
                sb.Append(password_space[random.Next(password_space.Length)]);
            }
            return sb.ToString();
        }

        /// <summary>
        /// Generates a random number
        /// </summary>
        /// <param name="length">length of the number</param>
        /// <returns></returns>
        public static string RandomNumeric(int length)
        {
            string password_space = DIGITS;
            StringBuilder sb = new StringBuilder(length);
            byte[] randomBytes = new byte[4];
            seedProvider.GetBytes(randomBytes);

            int seed = (randomBytes[0] & 0x7f) << 24 |
                        randomBytes[1] << 16 |
                        randomBytes[2] << 8 |
                        randomBytes[3];

            Random random = new Random(seed);
            for (int i = 0; i < length; i++)
            {
                sb.Append(password_space[random.Next(password_space.Length)]);
            }
            return sb.ToString();
        }

        public static DateTime RandomTime(DateTime min, DateTime max)
        {
            TimeSpan diff = max - min;

            long diffInt = diff.Ticks;

            long randomValue = RandomHelper.Random(diffInt);
            DateTime randomTime = new DateTime(min.Ticks + (long)randomValue);
            return randomTime;
        }

        public static int Random(int max)
        {
            return randomizer.Next(1, max);
        }

        public static long Random(long max)
        {
            return randomizer.RandomLong(1, max);
        }

    }

    //http://stackoverflow.com/questions/6651554/random-number-in-long-range-is-this-the-way
    static class RandomExtensions
    {
        public static long RandomLong(this Random rnd)
        {
            byte[] buffer = new byte[8];
            rnd.NextBytes(buffer);
            return BitConverter.ToInt64(buffer, 0);
        }

        public static long RandomLong(this Random rnd, long min, long max)
        {
            EnsureMinLEQMax(ref min, ref max);
            long numbersInRange = unchecked(max - min + 1);
            if (numbersInRange < 0)
                throw new ArgumentException("Size of range between min and max must be less than or equal to Int64.MaxValue");

            long randomOffset = RandomLong(rnd);
            if (IsModuloBiased(randomOffset, numbersInRange))
                return RandomLong(rnd, min, max); // Try again
            else
                return min + PositiveModuloOrZero(randomOffset, numbersInRange);
        }

        static bool IsModuloBiased(long randomOffset, long numbersInRange)
        {
            long greatestCompleteRange = numbersInRange * (long.MaxValue / numbersInRange);
            return randomOffset > greatestCompleteRange;
        }

        static long PositiveModuloOrZero(long dividend, long divisor)
        {
            long mod;
            Math.DivRem(dividend, divisor, out mod);
            if (mod < 0)
                mod += divisor;
            return mod;
        }

        static void EnsureMinLEQMax(ref long min, ref long max)
        {
            if (min <= max)
                return;
            long temp = min;
            min = max;
            max = temp;
        }
    }
}
